/*
 *  bbmath.h
 *  BioBreak
 *
 *  Created by terry franguiadakis on 2/19/09.
 *  Copyright 2009 __MyCompanyName__. All rights reserved.
 *
 */
#pragma once

#include <math.h>
#include "mathutils.h"
/*--------------------------------------------------------------------------*/
//	Constants

//const float pi = 3.14159265358979323846f;
const float piOver2 = M_PI * 0.5f;
const float piOver4 = M_PI * 0.25f;
const float piOver8 = M_PI * 0.125f;
const float naturalLogBase = 2.718281f;
const float twoPi = M_PI * 2;
const float piOver180 = M_PI / 180.0f;
const float halfCircle = 180.0f;
const float g180OverPi = 180.0f / M_PI; 
const float g_epsilonDefault = 0.01f;
const float g_epsilonHiRes = 0.0001f;
const float g_epsilonRadDefault = piOver180;

bool IsNear(float g0, float g1, float epsilon = g_epsilonDefault);
bool IsNearZero(float g0, float epsilon = g_epsilonDefault);

float GRandInRange(float min, float max);
int SRandInRange(int min, int max);		// in range [min, max] (both ends inclusive)
inline int SRand(int max)				// in range [0, max] (both ends inclusive)
        { return SRandInRange(0, max); }


// BB (TEF) I'm not sure why we need this, it's defined in the system

template <typename T, typename U>
inline T BBMIN(const T& t, const U& u)
    { return t < u ? t : u; }
template <typename T, typename U, typename V>
inline T BBMIN(const T& t, const U& u, const V& v)
    { return BBMIN(BBMIN(t, u), v); }

template <typename T, typename U>
inline T BBMAX(const T& t, const U& u)
    { return t < u ? u : t; }

template <typename T, typename U, typename V>
inline T BBMAX(const T& t, const U& u, const V& v)
    { return BBMAX(BBMAX(t, u), v); }


template<class T> 
T Clamp(T val, T minVal, T maxVal)
{
    return BBMIN(BBMAX(val, minVal), maxVal);
}


inline float Lerp(float a, float b, float t)
{
	return a + (b - a ) * t;
}

// math correct mod
inline float BMod(float value, float operand)
{
	float ret = fmod(value, operand);

	if (ret < 0)
	{
		return operand + ret;
	}
	return ret;
}

bool IsPowerof2(int value);

/*--------------------------------------------------------------------------*/
// Class Vector2f declaration.
class Vector2f 
{
public:
					Vector2f(const Vector2f& i_vec):x(i_vec.x), y(i_vec.y) {   }
    
					Vector2f(float i_x=0.0f, float i_y=0.0f):x(i_x), y(i_y) {   }
    
    void			Set(float i_x, float i_y) {
						x = i_x ; y = i_y; }
    
    inline float	Dot( const Vector2f &other ) const { 
                            return (x * other.x + y * other.y) ; }
	inline float	Length() const { 
                            return sqrtf(Dot(*this)) ; }
	inline float	LengthSqr() const { 
                            return Dot(*this); }
    inline Vector2f Normalized();
	inline void     Normalize();
	const Vector2f&	operator*=(float i_scalar)
					{
						x *= i_scalar;
						y *= i_scalar;
						return *this;
					}
	const Vector2f&	operator+=(const Vector2f& i_rhs)
					{
						x += i_rhs.x;
						y += i_rhs.y;
						return *this;
					}
	
	void			LimitToLength(float i_limit) 
					{
						float lenSq = LengthSqr();
						if(lenSq > i_limit*i_limit)
						{
							float ratio = i_limit / sqrt(lenSq);
							*this *= ratio;
						}
					}
    
	static const Vector2f Zero;
	static const Vector2f One;
	static const Vector2f XNormal;
	static const Vector2f YNormal;
	static const Vector2f XNormalNeg;
	static const Vector2f YNormalNeg;
    
    float x,y;
};

/*--------------------------------------------------------------------------*/

inline Vector2f operator*(const Vector2f& i_vec, float scalar)
{
    return Vector2f(i_vec.x * scalar, i_vec.y * scalar);
}
inline Vector2f operator+(const Vector2f& i_veca, const Vector2f& i_vecb)
{
    return Vector2f(i_veca.x + i_vecb.x, i_veca.y + i_vecb.y);
}
inline Vector2f operator-(const Vector2f& i_veca, const Vector2f& i_vecb)
{
    return Vector2f(i_veca.x - i_vecb.x, i_veca.y - i_vecb.y);
}
inline bool operator<(const Vector2f& i_veca, const Vector2f& i_vecb)
{
    return (i_veca.x < i_vecb.x) && (i_veca.y < i_vecb.y);
}
inline float Dist(const Vector2f& i_veca, const Vector2f& i_vecb)
{
    Vector2f result = i_veca - i_vecb;
    return result.Length();
}
inline Vector2f Vector2f::Normalized() 
{
    float length = 1.0f / Length() ;
    return (*this)* length;
}
inline void Vector2f::Normalize()
{
    Vector2f tmp = *this;
    float len = Length();
    
    Set(tmp.x/len,tmp.y/len);
}

inline bool IsNear(const Vector2f& v1, const Vector2f& v2, float episilon = 0.001f)
{
	Vector2f result = v2 - v1;
	return result.Length() <= episilon; 
}

inline Vector2f Lerp(const Vector2f& v1, const Vector2f& v2, float t)
{
	return Vector2f( Lerp(v1.x, v2.x, t), Lerp(v1.y, v2.y, t));
}

inline Vector2f Perp(const Vector2f& v1)
{
    return Vector2f(-v1.y, v1.x);
}

inline Vector2f Vec2fRandInRange(const Vector2f& i_min, const Vector2f& i_max)
{
    return Vector2f(::GRandInRange(i_min.x, i_max.x), ::GRandInRange(i_min.y, i_max.y));
}

/*--------------------------------------------------------------------------*/
// Class Color3f declaration.
class Color3f
{
public:
    Color3f(float i_r = 1.0f, float i_g = 1.0f, float i_b = 1.0f ):r(i_r), g(i_g), b(i_b) { }
    
    float r,g,b; 
};

inline Color3f operator*(const Color3f& i_color, float i_scalar)
{
    return Color3f(i_color.r * i_scalar, i_color.g * i_scalar, i_color.b * i_scalar);
}

/*--------------------------------------------------------------------------*/
// Class Color4f declaration.
class Color4f
{
public:
    Color4f(float i_r = 1.0f, float i_g = 1.0f, float i_b = 1.0f, float i_a = 1.0f):r(i_r), g(i_g), b(i_b), a(i_a) { }
	
	const Color4f& operator=(const Color3f&  i_rhs)
	{
		r = i_rhs.r;
		g = i_rhs.g;
		b = i_rhs.b;
        a = 1.0f;
		return *this;
	}
    
    float r, g, b, a; 

};

/*--------------------------------------------------------------------------*/

class CRangeF
{
public:
    CRangeF() {
        Set(0, 1); }
    CRangeF(float i_min, float i_max) {
        Set(i_min, i_max); }
    
    void		Set(float i_min, float i_max) {
                    m_min = i_min; m_max = i_max; /*assert(m_min <= m_max);*/ } // BB (TEF) need asserts
    void		SetMin(float i_min) {
                    m_min = i_min; /*assert(m_min <= m_max);*/ } // BB (TEF) need asserts here
    void		SetMax(float i_max) {
                    m_max = i_max; /*assert(m_min <= m_max);*/ } // BB (TEF) need asserts here
    
    float		GetMin() const {
                    return m_min; }
    float		GetMax() const {
                    return m_max; }
    
    bool        IsInLimit(float i_valueToTest) const {
                    return (i_valueToTest >= m_min) && (i_valueToTest <= m_max); }
    
    float			Constrain(float i_valueToConstrain) const {
                return BBMIN(BBMAX(i_valueToConstrain, m_min), m_max); }
    
    float			Lerp(float i_lerpScale) const { 
                return Constrain((m_min * (1.0f - i_lerpScale)) + (m_max * i_lerpScale));  }
    
    float         GetRandom() const {
                return ::GRandInRange(m_min, m_max); }
    
    static const CRangeF ZeroOne;
    
private:
    
    float 		m_min;
    float		m_max;
};

class CRangeI
    {
    public:
        CRangeI() {
                Set(0, 1); }
        CRangeI(int i_min, int i_max) {
                Set(i_min, i_max); }
        
        void		Set(int i_min, int i_max) {
                m_min = i_min; m_max = i_max; /*LCASSERT(m_min <= m_max);*/} // BB (TEF) get asserts back in
        void		SetMin(int i_min) {
                m_min = i_min; /*LCASSERT(m_min <= m_max);*/ } // BB (TEF) get asserts back in
        void		SetMax(int i_max) {
                m_max = i_max; /*LCASSERT(m_min <= m_max);*/ }
        
        int			GetMin() const {
                return m_min; } 
        int			GetMax() const {
                return m_max; }
        
        bool        IsInLimit(int i_valueToTest) const {
                return (i_valueToTest >= m_min) && (i_valueToTest <= m_max); }
        
        int			Constrain(int i_valueToConstrain) const {
                return BBMIN(BBMAX(i_valueToConstrain, m_min), m_max); }
        
        int         GetRandom() const {
                return ::SRandInRange(m_min, m_max); }
        

        static const CRangeI ZeroOne;
        
    private:
        
        int 		m_min;
        int			m_max;
};

#if (0)
class CRangeVec2F
    {
    public:
        CRangeF() {
                Set(Vector2f::Zero, Vector2f::One); }
        CRangeF(const Vector2f& i_min, const Vector2f& i_max) {
                Set(i_min, i_max); }
        
        void		Set(const Vector2f& i_min, const Vector2f& i_max) {
                        m_min = i_min; m_max = i_max; /*assert(m_min <=     m_max);*/ } // BB (TEF) need asserts
        void		SetMin(const Vector2f& i_min) {
                        m_min = i_min; /*assert(m_min <= m_max);*/ } // BB (TEF) need asserts here
        void		SetMax(const Vector2f& i_max) {
                        m_max = i_max; /*assert(m_min <= m_max);*/ } // BB (TEF) need asserts here
        
        const Vector2f&	GetMin() const {
                        return m_min; }
        const Vector2f&	GetMax() const {
                        return m_max; }
        
        bool        IsInLimit(const Vector2f& i_valueToTest) const {
                        return ((i_valueToTest.x >= m_min.x) && (i_valueToTest.x <= m_max.x)) && 
                                ((i_valueToTest.y >= m_min.y) && (i_valueToTest.y <= m_max.y)); }
        
        
        Vector2f	Lerp(float i_lerpScale) const { 
                        i_lerpScale = Clamp(i_lerpScale, 0.0f, 1.0f);
                        return Lerp(m_min * (1.0f - i_lerpScale)) + (m_max * i_lerpScale));  }
        
        Vector2f       GetRandom() const {
                        return Vec2fRandInRange(m_min, m_max); }
        
        static const CRangeF ZeroOne;
        
    private:
        
        float 		m_min;
        float		m_max;
    };
#endif

/*--------------------------------------------------------------------------*/
// Class Transform2D declaration.
class Transform2D
{
public:
    Transform2D():m_zrot(0.0f), m_scale(1.0f), m_depth(0.0f) {}
    
    void        PushMatrix() const;   // push this on to the stack
    void        PopMatrix() const;    // pop this on the stack
    void        PushMatrixTmap() const;
    void        PopMatrixTmap() const;
    // we need these for code that doesn't know about gl.
    static void glPushMatrix();
    static void glPopMatrix();
    static void glTranslatef(float i_x, float i_y, float i_z);
    static void glScalef(float i_scale);
    
    void        SetRotation(float i_zrot){ // rotation around z in rads.
                    m_zrot = i_zrot; }
    float       GetRotation() const {
                    return m_zrot;}
    void        SetScale(float i_scale) {
                    m_scale = i_scale;  }
    float       GetScale() const {
                    return m_scale;}
    Vector2f    GetPos() const {
                    return m_pos;   }
    void        SetPos(const Vector2f& i_pos) {
                    m_pos = i_pos;   }
    
    void        SetDepth(float i_depth) {
                    m_depth = i_depth;}
    float        GetDepth() const {
                    return m_depth; }

private:
    Vector2f m_pos;
    float    m_zrot;
    float    m_scale;
    float    m_depth;
};


/*--------------------------------------------------------------------------*/

